package org.app.appjson.reader;

import org.app.appjson.adapter.*;
import org.app.appjson.exception.AppJsonException;
import org.app.appjson.reader.constructor.ObjectConstructor;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

/**
 * 读处理器
 * @author lizhongsi
 * @version 1.0
 * @since 1.0
 */
public class ReadHandler {

    /* 词法分析器 */
    private final JsonLexer lexer;
    /* 保存用户传入的类型参数 */
    private final List<Class<?>> classes;
    /* classes的下标 */
    private int pos;
    /* 实例的类型 */
    private Class<?> classOfInstance;

    public ReadHandler(JsonLexer lexer, final Class<?>... classes) {
        this.lexer = lexer;
        this.classes = new ArrayList<>(4);
        /* 设置类型参数 */
        for (Class<?> c: classes) {
            this.classes.add(c);
            // 如果是一维数组则把ComponentType也存到classes，等待ArrayAdapter消费
            // eg: String[] 会存成L[java.lang.String和java.lang.String
            if (c.isArray()) {
                this.classes.add(c.getComponentType());
            }
        }
    }

    public JsonLexer getLexer() {
        return this.lexer;
    }

    /**
     * 根据传入参数、fromJson参数或json词法单元上下文，获取待创建对象的class类型
     * @param clazz 类型参数
     * @return 待创建对象的class类型
     */
    public Class<?> getObjectClass(final Class<?> clazz) throws AppJsonException {
        Class<?> result;
        if (clazz != null) {
            result = clazz;
            this.classOfInstance = clazz;
            return result;
        }
        // 根据fromJson方法的入参获取类型
        if (pos < this.classes.size()) {
            result = this.classes.get(pos);
            pos++;
            // 接口转换
            if (result.isInterface()) {
                if (Collection.class.isAssignableFrom(result)) {
                    if (Set.class.isAssignableFrom(result)) {
                        result = HashSet.class;
                    } else if (Queue.class.isAssignableFrom(result)) {
                        result = ArrayDeque.class;
                    } else {
                        result = ArrayList.class;
                    }
                } else if (Map.class.isAssignableFrom(result)) {
                    if (ConcurrentNavigableMap.class.isAssignableFrom(result)) {
                        result = ConcurrentSkipListMap.class;
                    } else {
                        result = HashMap.class;
                    }
                }
            }
            // 抽象类转换（基本类型不转换）
            else if (!result.isPrimitive() && Modifier.isAbstract(result.getModifiers())) {
                if (SortedSet.class.isAssignableFrom(result)) {
                    result = TreeSet.class;
                }
            }
            this.classOfInstance = result;
            return result;
        }
        // 根据上下文获取解析的类型
        Objects.requireNonNull(lexer, "lexer is null.");
        switch (lexer.peek().getType()) {
            case JsonToken.NULL:
            case JsonToken.BOOLEAN: result = Boolean.class; break;
            case JsonToken.STRING: result = String.class; break;
            case JsonToken.NUMBER: result = Number.class; break;
            case JsonToken.BEGIN_OBJECT: result = HashMap.class; break;
            case JsonToken.BEGIN_ARRAY: result = ArrayList.class; break;
            default: throw new AppJsonException("Cannot get a class by json context.");
        }
        classOfInstance = result; //保存“下一个将要创建的实例”的class类型
        return result;
    }

    /**
     * 获取instanceClass的实例
     * @return classOfInstance的实例
     * @param <T> 泛型参数
     */
    public <T> T newInstance() throws AppJsonException {
        Objects.requireNonNull(this.classOfInstance, "instanceClass is null.");
        // 常用类型的对象创建，使用switch底层哈希表比else-if快一点
        switch (classOfInstance.getName()) {
            case "java.util.HashMap": return (T) new HashMap<>();
            case "java.util.ArrayList": return (T) new ArrayList<>();
            case "java.util.LinkedList": return (T) new LinkedList<>();
            case "java.util.TreeSet": return (T) new TreeSet<>();
            case "java.util.LinkedHashSet": return (T) new LinkedHashSet<>();
            case "java.util.concurrent.ConcurrentMap": return (T) new ConcurrentHashMap<>();
            case "java.util.TreeMap": return (T) new TreeMap<>();
        }
        try {
            if (! classOfInstance.isMemberClass()) {// 非内部类，直接创建实例
                return (T) classOfInstance.newInstance();
            } else {// 静态内部类，调用无参构造器返回实例
                Constructor<T> constructor = (Constructor<T>) classOfInstance.getDeclaredConstructor();
                constructor.setAccessible(true);
                return constructor.newInstance();
            }
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
            // 非静态内部类，调用第三方库构造实例
            ObjectConstructor<T> objectConstructor = new ObjectConstructor<>();
            return objectConstructor.construct(classOfInstance);
        }catch (Exception e) {
            e.printStackTrace();
            throw new AppJsonException("Can not create an instance." + classOfInstance.getName());
        }
    }

    @SuppressWarnings({"unchecked"})
    public <T> T doRead() throws AppJsonException {
        return (T) doRead(getObjectClass(null));
    }

    /**
     * 调用适配器递归读取和处理JsonReadData
     * @param classOfT 类型参数
     * @return 结果
     * @param <T> 泛型参数
     */
    @SuppressWarnings({"unchecked"})
    public <T> T doRead(Class<T> classOfT) throws AppJsonException {
        Objects.requireNonNull(classOfT, "classOfT is null.");
        if(Collection.class.isAssignableFrom(classOfT)) {
            return (T) CollectionAdapter.read(this);
        } else if(int.class.isAssignableFrom(classOfT) || Integer.class.isAssignableFrom(classOfT)) {
            return (T) IntegerAdapter.read(this);
        } else if (Boolean.class.isAssignableFrom(classOfT) || boolean.class.isAssignableFrom(classOfT)) {
            return (T) BooleanAdapter.read(this);
        } else if(byte.class.isAssignableFrom(classOfT) || Byte.class.isAssignableFrom(classOfT)) {
            return (T) ByteAdapter.read(this);
        } else if(short.class.isAssignableFrom(classOfT) || Short.class.isAssignableFrom(classOfT)) {
            return (T) ShortAdapter.read(this);
        } else if(long.class.isAssignableFrom(classOfT) || Long.class.isAssignableFrom(classOfT)) {
            return (T) LongAdapter.read(this);
        } else if(float.class.isAssignableFrom(classOfT) || Float.class.isAssignableFrom(classOfT)) {
            return (T) FloatAdapter.read(this);
        } else if(double.class.isAssignableFrom(classOfT) || Double.class.isAssignableFrom(classOfT)) {
            return (T) DoubleAdapter.read(this);
        } else if (classOfT.isArray()) {
            return (T) ArrayAdapter.read(this);
        } else if (Number.class.isAssignableFrom(classOfT)) {
            return (T) NumberAdapter.read(this);
        } else if (String.class.isAssignableFrom(classOfT)) {
            return (T) StringAdapter.read(this);
        } else if (Map.class.isAssignableFrom(classOfT)) {
            return (T) MapAdapter.read(this);
        } else if(java.util.Date.class.isAssignableFrom(classOfT)) {
            if(java.sql.Date.class.isAssignableFrom(classOfT)) {
                return (T) SqlDateAdapter.read(this);
            } else if(java.sql.Timestamp.class.isAssignableFrom(classOfT)) {
                return (T) TimestampAdapter.read(this);
            } else {
                return (T) DateAdapter.read(this);
            }
        } else {
            return (T) ReflectiveAdapter.read(this);
        }
    }
}
